package com.woniu.cms.gateway.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.woniu.common.api.Result;
import com.woniu.common.api.constant.RedisTokenConstant;
import com.woniu.common.api.status.Impl.ResultCode;
import com.woniu.common.api.util.JWTUtil;
import com.woniu.common.api.util.TokenEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component //必须要注册到ioc容器中
public class AuthFilter implements GlobalFilter, Ordered {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     *
     * @param exchange 服务器交换机，封装了请求，响应，路径等内容
     * @param chain provides a way to delegate to the next filter
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String requestPath = request.getURI().getPath();


        //封装了对路径的判断
        //因为如果用字符串的contains，authaaa也会被匹配到
        AntPathMatcher pathMatcher = new AntPathMatcher();
        boolean match = pathMatcher.match("/**/auth/**", requestPath);
        if(match){
            //校验freshToken
            List<String> refreshTokenList = request.getHeaders().get("refreshToken");
            if(refreshTokenList == null || refreshTokenList.size() == 0) {
                return error(response);
            }
            //refreshToken有，但是可能非法或者过期(过期就是redis中key的时间到了，key被清除)
            String refreshToken = refreshTokenList.get(0);
            log.warn("拿到的refreshToken:{}", refreshToken);
            String refreshTokenKey = RedisTokenConstant.REFRESH_TOKEN_PRE.getStatus() + refreshToken;
            if(!redisTemplate.hasKey(refreshTokenKey)){
                //refreshToken非法或过期
                return error(response);
            }


            //校验token
            String token = request.getHeaders().getFirst("Authorization");
            token = token.replace(RedisTokenConstant.TOKEN_PRE.getStatus(), "");
            log.warn("拿到的token:{}", token);
            //token为空
            if(StringUtils.isEmpty(token)){
                return error(response);
            }
            //token不为空
            if(JWTUtil.verify(token) == TokenEnum.TOKEN_BAD){
                log.error("网关token校验失败");
                return error(response);
            }
            if(JWTUtil.verify(token) == TokenEnum.TOKEN_EXPIRE){
                //token过期，但是refreshToken没有过期
                //重新生成Token
                token = JWTUtil.generateToken(JWTUtil.getUname(token));
                //将token放到redis，同时刷新refreshToken过期时间
                redisTemplate.opsForValue().set(RedisTokenConstant.REFRESH_TOKEN_PRE.getStatus() + refreshToken, token, JWTUtil.REFRESH_TOKEN_EXPIRE_TIME, TimeUnit.MILLISECONDS);
                //将token返回给前端,并暴露头
                response.getHeaders().add("authorization", token);
                response.getHeaders().add("Access-Control-Expose-Headers","authorization");

                //将request中的token换成新的不过期的token
                // 将新的token转发给微服务
                ServerHttpRequest.Builder requestBuilder = request.mutate();
                // 先删除，后新增
                requestBuilder.headers(k -> k.remove("authorization"));
                requestBuilder.header("authorization", token);
                ServerHttpRequest requestNew = requestBuilder.build();
                exchange.mutate().request(requestNew).build();

            }
            //到这一步说明token正确或者虽然过期但是已经生成了新token
            //解析token
            String userName = JWTUtil.getUname(token);
            log.warn("userName:{}", userName);

            //token验证通过之后把userName放到request中
            ServerHttpRequest.Builder mutate = request.mutate();
            mutate.header("userName", userName);


        }

        return chain.filter(exchange);
    }

    private Mono<Void> error(ServerHttpResponse response) {
        ResultCode permissionNoAccess = ResultCode.PERMISSION_NO_ACCESS;
        Result<Object> result = Result.error(permissionNoAccess);

        //这里是响应式的response，所以需要先转换数据为json的byte类型数组
        try {
            //writeValueAsBytes转换为json的byte数组，默认使用utf-8
            byte[] bytes = new ObjectMapper().writeValueAsBytes(result);

            //将数据进行封装 wrap(包裹)
            DataBuffer buffer = response.bufferFactory().wrap(bytes);
            //设置响应头
            response.getHeaders().add("Context-Type", "application/json;charset=utf-8");
            //返回数据并终止请求(因为走这里就不走过滤器链放行了)
            return response.writeWith(Mono.just(buffer));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }
}